package com.sudo.boot.admin._sys;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.mybatisflex.core.paginate.Page;
import com.sudo.boot.base.constant.enums.EnumStatus;
import com.sudo.boot.base.exception.BizException;
import com.sudo.boot.base.exception.ValidateException;
import com.sudo.boot.base.pojo.JsonRes;
import com.sudo.boot.base.util.ThreadLocalUtil;
import com.sudo.boot.common.util.task.BaseTaskRegistry;
import com.sudo.boot.common.util.task.TaskMethod;
import com.sudo.boot.common.util.task.TaskUtil;
import com.sudo.boot.mapper.sys.entity.SysLocalJob;
import com.sudo.boot.mapper.sys.model.bo.*;
import com.sudo.boot.ratelimit.annotation.ApiIdempotent;
import com.sudo.boot.service.sys.SysLocalJobService;
import com.sudo.boot.service.sys.jobservice.tldata.ThreadLocalObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("sys-local-job")
public class SysLocalJobController {

    @Resource
    private SysLocalJobService sysLocalJobService;

    @GetMapping("/page")
    public JsonRes<Page<SysLocalJob>> page(@Validated SysLocalJobBoQuery bo) {
        Page<SysLocalJob> page = sysLocalJobService.querySysLocalJobByPage(bo);
        return JsonRes.ok(page);
    }

    @SaCheckPermission("localJob_detail")
    @GetMapping("/detail/{id}")
    public JsonRes<SysLocalJob> detail(@PathVariable(value = "id") Integer id) {
        SysLocalJob sysLocalJob = sysLocalJobService.querySysLocalJobById(id);
        return JsonRes.ok(sysLocalJob);
    }

    @GetMapping("/localJobDocumentMap")
    public JsonRes<Map<String, Map<String, TaskMethod>>> localJobDocumentMap() {
        Map<String, Map<String, TaskMethod>> taskMethodMap = sysLocalJobService.getMethodAndParamCountList();
        return JsonRes.ok(taskMethodMap);
    }

    private void validateLocalJob(String beanName, String methodName, String methodParams,String cronExpression){
        if (StringUtils.hasText(cronExpression)) {
            boolean validExpression = CronExpression.isValidExpression(cronExpression);
            if (!validExpression) {
                throw new ValidateException("cron表达式不正确");
            }
        }
        Class<?> aClass = BaseTaskRegistry.getClassByBean(beanName);
        if (!StringUtils.hasText(methodParams)) {
            try{
                aClass.getDeclaredMethod(methodName);
            }catch (Exception e){
                throw new ValidateException("不存在该 "+ methodName +"() 类型的方法");
            }
        }else {
            try {
                aClass.getDeclaredMethod(methodName, Map.class);
            }catch (Exception e){
                throw new ValidateException("不存在该 "+ methodName +"(Map map) 类型的方法");
            }

            Map<String,Object> paramMap = JSON.parseObject(methodParams, new TypeReference<HashMap<String, Object>>(){});
            if (null == paramMap) {
                throw new ValidateException("参数格式异常，请保持Json对象格式");
            }
            boolean paramStatus = TaskUtil.verifyParam(beanName,methodName,paramMap);
            if (!paramStatus){
                throw new ValidateException("参数格式异常，请严格遵从参数类型、是否必填");
            }
        }

    }

    @ApiIdempotent
    @SaCheckPermission("localJob_add")
    @PostMapping("/add")
    public JsonRes<Void> add(@RequestBody @Validated SysLocalJobBoSave jobSaveBO) {
        // 子任务必须是已有任务，且不能为自己
        String nextJobIds = sysLocalJobService.verifySysLocalJobExist(-1, jobSaveBO.getNextJobIds());
        jobSaveBO.setNextJobIds(nextJobIds);

        // 验证任务
        validateLocalJob(jobSaveBO.getBeanName(), jobSaveBO.getMethodName(), jobSaveBO.getMethodParams(),jobSaveBO.getCronExpression());
        sysLocalJobService.saveSysLocalJob(jobSaveBO);
        return JsonRes.ok();
    }

    @ApiIdempotent
    @SaCheckPermission("localJob_status")
    @PostMapping("/status")
    public JsonRes<Void> status(@RequestBody @Validated SysLocalJobBoEditStatus bo) {
        sysLocalJobService.updateSysLocalJobStatus(bo);
        return JsonRes.ok();
    }

    @ApiIdempotent
    @SaCheckPermission("localJob_edit")
    @PostMapping("/updateWithoutStatus")
    public JsonRes<Void> updateWithoutStatus(@RequestBody @Validated SysLocalJobBoEditNoStatus bo) {
        // 子任务必须是已有任务，且不能包含自己
        String nextJobIds = sysLocalJobService.verifySysLocalJobExist(bo.getId(),bo.getNextJobIds());
        bo.setNextJobIds(nextJobIds);

        SysLocalJob sysLocalJob = sysLocalJobService.querySysLocalJobById(bo.getId());
        if (sysLocalJob == null){
            throw new BizException("不存在该任务");
        }
        if (EnumStatus.ON.getCode().equals(sysLocalJob.getStatus())) {
            throw new BizException("修改任务前必须停止该任务");
        }
        // 验证任务
        validateLocalJob(sysLocalJob.getBeanName(), sysLocalJob.getMethodName(), bo.getMethodParams(),bo.getCronExpression());

        sysLocalJobService.updateSysLocalJobWithNoStatus(bo);
        return JsonRes.ok();
    }

    @ApiIdempotent
    @SaCheckPermission("localJob_remove")
    @PostMapping("/remove/{id}")
    public JsonRes<Void> remove(@PathVariable Integer id) {
        SysLocalJob sysLocalJob = sysLocalJobService.querySysLocalJobById(id);
        if (EnumStatus.ON.getCode().equals(sysLocalJob.getStatus())) {
            return JsonRes.errorMsg("删除任务前必须停止该任务");
        }
        sysLocalJobService.deleteSysLocalJob(id);
        return JsonRes.ok();
    }

    @ApiIdempotent
    @SaCheckPermission("localJob_run")
    @PostMapping("/run")
    public JsonRes<Void> run(@RequestBody @Validated SysLocalJobBoRun bo) throws Exception {
        SysLocalJob sysLocalJob = sysLocalJobService.querySysLocalJobById(bo.getId());
        // 子任务必须是已有任务，且不能包含自己
        sysLocalJobService.verifySysLocalJobExist(sysLocalJob.getId(), sysLocalJob.getNextJobIds());
        // 验证任务
        validateLocalJob(sysLocalJob.getBeanName(), sysLocalJob.getMethodName(), bo.getMethodParams(),null);

        sysLocalJob.setMethodParams(bo.getMethodParams());
        Map<String, Object> jobConfigMap = ThreadLocalObject.initJobConfigMap(sysLocalJob);
        ThreadLocalUtil.setTlMap(jobConfigMap);
        TaskUtil.callTaskMethod(sysLocalJob.getBeanName(), sysLocalJob.getMethodName(), sysLocalJob.getMethodParams());
        return JsonRes.ok();
    }

}
